﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using Microsoft.CSharp;
using System.IO;
using System.CodeDom.Compiler;

namespace BoxSharp
{
    public class BoxSharpAddonManager
    {
        private BoxSharpAddonManager()
        {
        }

        private static BoxSharpAddonManager singleton = new BoxSharpAddonManager();

        public static BoxSharpAddonManager Singleton
        {
            get
            {
                return singleton;
            }
        }

        private List<BoxSharpAddon> addons = new List<BoxSharpAddon>();
        private TextBox theBox = null;

        public void Initialize(Window mainWindow, StackPanel mainPanel, StackPanel panel1, StackPanel panel2, TextBox box)
        {
            theBox = box;

            DirectoryInfo addonsDir = new DirectoryInfo(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Interface\\Addons\\"));
            DirectoryInfo[] addonSubDirs = addonsDir.GetDirectories();

            foreach (DirectoryInfo subDir in addonSubDirs)
            {
                BoxSharpAddon newAddon = new BoxSharpAddon(subDir.FullName, mainWindow, mainPanel, panel1, panel2, box);
                addons.Add(newAddon);
            }

            List<BoxSharpAddon> toDelete = new List<BoxSharpAddon>();

            foreach (BoxSharpAddon addon in addons)
            {
                if (!addon.Initialize())
                {
                    toDelete.Add(addon);
                }
            }

            foreach (BoxSharpAddon addon in toDelete)
            {
                addons.Remove(addon);
            }
        }

        public void OnCommand(string command)
        {
            if (command == "") return;

            string cleared = null;

            foreach (BoxSharpAddon addon in addons)
            {
                if (theBox.Text != command)
                {
                    cleared = theBox.Text;
                }

                theBox.Text = command;
                addon.OnCommand(command);
            }

            if (cleared != null)
            {
                theBox.Text = cleared;
            }
        }

        public void Exit()
        {
            foreach (BoxSharpAddon addon in addons)
            {
                addon.Exit();
            }
        }
    }

    public class BoxSharpAddon
    {
        private string name;
        private string codePath;
        private AddonInterface addonInterface;
        private Window window;
        private StackPanel panel;
        private StackPanel panel_1;
        private StackPanel panel_2;
        private TextBox box;

        public BoxSharpAddon(string path, Window mainWindow, StackPanel mainPanel, StackPanel panel1, StackPanel panel2, TextBox mainBox)
        {
            codePath = path;
            window = mainWindow;
            panel = mainPanel;
            panel_1 = panel1;
            panel_2 = panel2;
            box = mainBox;
        }

        public bool Initialize()
        {
            DirectoryInfo addonDir = new DirectoryInfo(codePath);

            name = addonDir.Name;

            FileInfo[] codeFiles = addonDir.GetFiles("*.cs");

            string programDir = System.Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles);
            List<string> externalRef = new List<string>();
            externalRef.Add(@"System.dll");
            externalRef.Add(@"System.Web.dll");
            externalRef.Add(@"System.Data.dll");
            externalRef.Add(@"System.Xml.dll");
            externalRef.Add(@"BoxSharpAddon.dll");
            externalRef.Add(Path.Combine(programDir, @"Reference Assemblies\Microsoft\Framework\v3.0\PresentationCore.dll"));
            externalRef.Add(Path.Combine(programDir, @"Reference Assemblies\Microsoft\Framework\v3.0\PresentationFramework.dll"));
            externalRef.Add(Path.Combine(programDir, @"Reference Assemblies\Microsoft\Framework\v3.5\System.Core.dll"));
            externalRef.Add(Path.Combine(programDir, @"Reference Assemblies\Microsoft\Framework\v3.5\System.Data.DataSetExtensions.dll"));
            externalRef.Add(Path.Combine(programDir, @"Reference Assemblies\Microsoft\Framework\v3.5\System.Xml.Linq.dll"));
            externalRef.Add(Path.Combine(programDir, @"Reference Assemblies\Microsoft\Framework\v3.0\UIAutomationProvider.dll"));
            externalRef.Add(Path.Combine(programDir, @"Reference Assemblies\Microsoft\Framework\v3.0\WindowsBase.dll"));

            if (File.Exists(Path.Combine(codePath, "assemblies.txt")))
            {
                string[] lines = File.ReadAllLines(Path.Combine(codePath, "assemblies.txt"));

                foreach (string line in lines)
                {
                    if (line.Length > 0)
                    {
                        string assem = "";
                        if (Path.IsPathRooted(line))
                        {
                            assem = line;
                        }
                        else
                        {
                            assem = Path.Combine(codePath, line);
                        }

                        if (!externalRef.Contains(assem))
                        {
                            externalRef.Add(assem);

                            try
                            {
                                File.Copy(assem, Path.Combine(AppDomain.CurrentDomain.BaseDirectory, Path.GetFileName(assem)), true);
                            }
                            catch (System.Exception)
                            {
                            }
                        }
                    }
                }
            }

            if (codeFiles.Length == 0)
            {
                return false;
            }

            string[] codeFileNames = new string[codeFiles.Length];

            for (int i = 0; i < codeFiles.Length; ++i)
            {
                codeFileNames[i] = codeFiles[i].FullName;
            }

            CSharpCodeProvider codeProvider = new CSharpCodeProvider();

            CompilerParameters codeParams = new System.CodeDom.Compiler.CompilerParameters();
            codeParams.GenerateExecutable = false;
            codeParams.GenerateInMemory = true;
            codeParams.IncludeDebugInformation = true;
            codeParams.TreatWarningsAsErrors = false;
            foreach (string asm in externalRef)
            {
                codeParams.ReferencedAssemblies.Add(asm);
            }

            CompilerResults compilerResults = codeProvider.CompileAssemblyFromFile(codeParams, codeFileNames);

            if (compilerResults.Errors.Count > 0)
            {
                string errorText = "";
                int errorCount = 0;

                foreach (CompilerError error in compilerResults.Errors)
                {
                    if (!error.IsWarning)
                    {
                        errorCount++;
                    }

                    errorText += error.ToString() + "\r\n\r\n";
                }

                if (errorCount > 0)
                {
                    MessageBox.Show(name + " failed to load. Here's the error message:\r\n\r\n" + errorText, "Error", MessageBoxButton.OK, MessageBoxImage.Error);
                    return false;
                }
                else
                {
                    MessageBox.Show(name + " has got some compiler warnings. Here's the warning message:\r\n\r\n" + errorText, "Warning", MessageBoxButton.OK, MessageBoxImage.Information);
                }
            }

            Type[] types = compilerResults.CompiledAssembly.GetExportedTypes();

            Type entryType = null;

            foreach (Type type in types)
            {
                if (type.BaseType == typeof(AddonInstance))
                {
                    entryType = type;
                    break;
                }
            }

            if (entryType == null)
            {
                MessageBox.Show(name + "failed to load. Entry class not found.");
                return false;
            }

            AddonInstance instance = compilerResults.CompiledAssembly.CreateInstance(entryType.FullName) as AddonInstance;

            addonInterface = new AddonInterface(name, codePath, window, panel, panel_1, panel_2, box);

            instance.AddonMain(addonInterface);

            addonInterface.OnInitialize();

            return true;
        }

        public void OnCommand(string command)
        {
            addonInterface.DoCommand(command);
        }

        public void Exit()
        {
            addonInterface.OnFinalize();
        }
    }

    public delegate void AddonEventHandler();
    public delegate void CommandEventHandler(string bscommand);

    public abstract class AddonInstance
    {
        public virtual void AddonMain(AddonInterface addonInterface)
        {
        }
    }

    public class AddonInterface
    {
        private string name;
        private string path;
        private Window window;
        private StackPanel panel;
        private StackPanel panel_1;
        private StackPanel panel_2;
        private TextBox box;

        public AddonInterface(string addonName, string addonPath, Window mainWindow, StackPanel mainPanel, StackPanel panel1, StackPanel panel2, TextBox mainBox)
        {
            name = addonName;
            path = addonPath;
            window = mainWindow;
            panel = mainPanel;
            panel_1 = panel1;
            panel_2 = panel2;
            box = mainBox;
        }

        public string Name
        {
            get { return name; }
        }

        public string Path
        {
            get { return path; }
        }

        public Window MainWindow
        {
            get { return window; }
        }

        public StackPanel MainPanel
        {
            get { return panel; }
        }

        public StackPanel Panel1
        {
            get { return panel_1; }
        }

        public StackPanel Panel2
        {
            get { return panel_2; }
        }

        public TextBox Box
        {
            get { return box; }
        }

        public void OnInitialize()
        {
            if (AddonInitializing != null)
            {
                AddonInitializing();
            }
        }

        public void OnFinalize()
        {
            if (AddonFinalizing != null)
            {
                AddonFinalizing();
            }
        }

        public void DoCommand(string command)
        {
            if (OnCommand != null)
            {
                OnCommand(command);
            }
        }

        public void SendCommand(string command)
        {
            BoxSharpAddonManager.Singleton.OnCommand(command);
        }

        public event CommandEventHandler OnCommand;
        public event AddonEventHandler AddonInitializing;
        public event AddonEventHandler AddonFinalizing;
    }
}
